#pragma once
#include "detail.hpp"
#include "abstract.hpp"
#include "field.hpp"
#include "message.hpp"
#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/net/Buffer.h>
#include <muduo/net/TcpClient.h>
#include <muduo/net/Buffer.h>
#include <muduo/base/CountDownLatch.h>
#include <muduo/net/EventLoopThread.h>
#include <mutex>
#include <unordered_map>

// 先进行basebuff具象层实现 ,如果以后不想用muduo实现可以在MuduoBuffer中改变
namespace RPC_project
{
    class MuduoBuffer : public BaseBuffer
    {
    public:
        using ptr = std::shared_ptr<MuduoBuffer>;

        MuduoBuffer(muduo::net::Buffer *buff) : _buff(buff)
        {
        }
        virtual size_t ReadableBytes() override
        {
            return _buff->readableBytes();
        }
        virtual int32_t PeekInt32() override // 从缓冲区中取出4个字节 ,不从缓冲区删除
        {
            // muduo是一个网络库，从缓冲区取出一个4字节整形 ，会进行网络字节序的转换
            return _buff->peekInt32();
        }
        virtual void RetrieveInt32() override // 从缓冲区删除前四个数据
        {
            return _buff->retrieveInt32();
        }
        virtual int32_t ReadInt32() override // 从缓冲区中取出4个字节 ,并且从缓冲区将其删除
        {
            return _buff->readInt32();
        }
        virtual std::string RetrieveAsString(size_t size) override // 取出定长字符串
        {
            return _buff->retrieveAsString(size);
        }

    private:
        muduo::net::Buffer *_buff; // 指向connection中的缓冲区，不能随意释放，只是实现MuduoBuffer的功能
    };

    class BufferFactory
    {
    public:
        template <typename... Args>
        static BaseBuffer::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoBuffer>(std::forward<Args>(args)...);
        }
    };

    class LVProtocol : public BaseProtocol
    {
    public:
        // LV格式解决粘包问题
        //   | len | val |
        //   | len | mtype | IDlength | ID  |body |
        using ptr = std::shared_ptr<LVProtocol>;
        // 消息是否完整，是否够一条消息处理的长度
        virtual bool CanProcessed(const BaseBuffer::ptr &ptr) override
        {
            if (ptr->ReadableBytes() < lenFieldsLength)
            {
                return false;
            }
            int32_t total_len = ptr->PeekInt32();
            if (ptr->ReadableBytes() < (total_len + lenFieldsLength))
                return false;

            return true;
        }
        // CanProcessed返回true时 才能调用OnMessage
        //  从buffer中提取消息
        virtual bool OnMessage(const BaseBuffer::ptr &ptr, BaseMessage::ptr &msgptr) override
        {
            int32_t total_len = ptr->ReadInt32();  // 总长度
            MType mtype = (MType)ptr->ReadInt32(); //!!!已经将网络字节序转成了主机字节序
            int32_t idlen = ptr->ReadInt32();
            int32_t bodylen = total_len - mtypeFieldsLength - IDlenFieldsLength - idlen;
            std::string id = ptr->RetrieveAsString(idlen);
            std::string body = ptr->RetrieveAsString(bodylen);
            msgptr = MessageFactory::Create(mtype);
            if (msgptr.get() == nullptr)
            {
                ELOG("OnMessage构造对象失败\n");
                return false;
            }
            bool ret = msgptr->Deserialize(body); // 将body反序列化放入msgptr
            if (ret == false)
            {
                ELOG("OnMessage反序列化失败\n");
                return false;
            }
            // 便于消息追踪(id) ,消息辨别
            msgptr->Setid(id);
            msgptr->SetMyType(mtype);

            return true;
        }

        virtual std::string Serialize(const BaseMessage::ptr &msgptr) override // 对消息序列化（先将body序列化 ，之后加上lv结构，在序列化成字符串）返回字符串
        {

            // 从body 变成->  | len | mtype | IDlength | ID  |body |
            std::string body = msgptr->Serialize();
            auto mtype = htonl((int32_t)msgptr->MyType()); // 将主机字节序转成网络字节序
            std::string id = msgptr->Rid();
            int32_t idlen = id.size();
            int32_t n_idlen = htonl(idlen);
            int32_t total_len = mtypeFieldsLength + IDlenFieldsLength + idlen + body.size();
            int32_t n_total_len = htonl(total_len);
            std::string result;

            result.reserve(total_len);
            result.append((char *)&n_total_len, lenFieldsLength); //(char*)逐字节访问
            result.append((char *)&mtype, mtypeFieldsLength);
            result.append((char *)&n_idlen, IDlenFieldsLength);
            result.append(id);
            result.append(body);
            return result;
        }

    private:
        const size_t lenFieldsLength = 4;
        const size_t mtypeFieldsLength = 4;
        const size_t IDlenFieldsLength = 4;
    };
    // 工厂模式便于代码维护
    class LVProtocolFactory
    {
    public:
        template <typename... Args>
        static BaseProtocol::ptr create(Args &&...args)
        {
            return std::make_shared<LVProtocol>(std::forward<Args>(args)...);
        }
    };

    class MuduoConnection : public BaseConnection
    {
    public:
        using ptr = std::shared_ptr<MuduoConnection>;
        // 不需要给每一个connection都创建protocol
        // 只需要在server和client里构造protocol
        MuduoConnection(const muduo::net::TcpConnectionPtr &conn, const BaseProtocol::ptr &protocol)
            : _protocol(protocol), _conn(conn)
        {
        }

        virtual void Send(const BaseMessage::ptr &ptr) override // 用_protocol的接口将消息序列化，再将消息发送
        {
            std::string msg = _protocol->Serialize(ptr); // 加上lv格式从body变成| len | mtype | IDlength | ID  |body |
            _conn->send(msg);
        }
        virtual void Shutdown() override // 关闭连接
        {
            _conn->shutdown();
        }

        virtual bool Connected() override
        {
            _conn->connected();
            return true;
        }

    private:
        BaseProtocol::ptr _protocol;
        muduo::net::TcpConnectionPtr _conn; // 实现消息的发布
    };

    class MuduoConnectionFactory
    {
    public:
        template <typename... Args>
        static BaseConnection::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoConnection>(std::forward<Args>(args)...);
        }
    };

    class MuduoServer : public BaseServer
    {
    public:
        using ptr = std::shared_ptr<MuduoServer>;

        MuduoServer(int port) // 可以转到TcpServer源码去看看
            : _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port),
                      "MuduoServer", muduo::net::TcpServer::kReusePort) // muduo::net::TcpServer::kReusePort开启地址重用
              ,_protocol(LVProtocolFactory::create())
        {
        }

        virtual void start() override // 服务器的搭建
        {
            // 用bind解决多一个this指针问题
            // 设置回调函数
            _server.setConnectionCallback(std::bind(&MuduoServer::onConnection, this, std::placeholders::_1));
            _server.setMessageCallback(std::bind(&MuduoServer::onMessage, this,
                                                 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            // 启动服务器
            _server.start();  // 开始监听
            _baseloop.loop(); // 先监听,再开始事件监控 ---内部是死循环 ,服务器可以这样使用 ,客户端不能这样使用(客户端中需要单独创建线程)
        }

    private:
        void onConnection(const muduo::net::TcpConnectionPtr &conn) // 链接建立时/断开时的回调方法
        {
            // 管理连接，因为我们往后的处理是基于Muduoconnection而不是TcpConnection
            if (conn->connected())
            {
                std::cout << "链接建立\n";
                // 来了一个连接就创建一个MuduoConnection加入到_conns中
                auto muduo_conn = MuduoConnectionFactory::create(conn, _protocol);
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _conns.insert(std::make_pair(conn, muduo_conn));
                }
                if (_cb_connection)
                    _cb_connection(muduo_conn); // 设置了回调就调用
            }
            else
            {

                std::cout << "链接断开\n";
                BaseConnection::ptr muduo_conn;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _conns.find(conn);
                    if (it == _conns.end())
                    {
                        return;
                    }
                    muduo_conn = it->second;
                    // 断开连接就删除连接信息
                    _conns.erase(conn);
                }
                if (_cb_close)
                    _cb_close(muduo_conn);
            }
        }

        // 消息处理函数
        void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buff, muduo::Timestamp)
        {
            auto base_buff = BufferFactory::create(buff);
            while (1)
            {
                // 如果不能处理（返回 false），则进入内部逻辑：
                // 检查缓冲区中的可读数据是否超过了maxDataSize
                // 如果超过最大值，就关闭连接并记录 "缓冲区中数据过大" 的错误日志，然后返回     //防止恶意多次攻击
                // 如果没超过最大值，则执行break退出当前循环（等待更多数据加载直到CanProcessed确认够一条消息的长度即可）
                if (_protocol->CanProcessed(base_buff) == false)
                {
                    // 数据不足
                    // 防备缓冲区中数据有很多很多,但是因为数据错误,导致数据又不足一条完整消息的情况 , 这种情况直接关闭连接
                    if (base_buff->ReadableBytes() > maxDataSize)
                    {
                        conn->shutdown();
                        ELOG("缓冲区中数据过大");
                        return;
                    }
                    break;
                }

                // 能够处理数据
                BaseMessage::ptr msg;
                bool ret = _protocol->OnMessage(base_buff, msg); // 将LV格式去掉 ,只有body部分
                if (ret == false)
                {
                    // 数据有问题
                    conn->shutdown();
                    ELOG("缓冲区中数据错误");
                    return;
                }
                // 使用muduoconnection
                BaseConnection::ptr base_conn;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _conns.find(conn);
                    if (it == _conns.end())
                    {
                        conn->shutdown(); // 连接在管理的连接中找不到 ,证明有问题
                        return;
                    }
                    base_conn = it->second;
                }
                // 调用回调函数进行消息处理,这里的回调是
                if (_cb_message)
                    _cb_message(base_conn, msg);
            }
        }

    private:
        const size_t maxDataSize = (1 << 16); // 64字节
        muduo::net::EventLoop _baseloop;      // EventLoop要放在TcpServer前面 ,要用EventLoop来初始化TcpServer
        muduo::net::TcpServer _server;
        BaseProtocol::ptr _protocol;
        std::mutex _mutex;
        // 管理连接，因为我们往后的处理是基于Muduoconnection而不是TcpConnection ,Muduoconnection是我们的具象层
        std::unordered_map<muduo::net::TcpConnectionPtr, BaseConnection::ptr> _conns;
    };

    class ServerFactory
    {
    public:
        template <typename... Args>
        static BaseServer::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoServer>(std::forward<Args>(args)...);
        }
    };

    class MuduoClient : public BaseClient
    {
    public:
        using ptr = std::shared_ptr<MuduoClient>;
        MuduoClient(const std::string &sip, int port)
            : _loopthread() // 一旦实例化好了,内部自动实现循环监控
              ,
              _protocol(LVProtocolFactory::create()), _baseloop(_loopthread.startLoop()), _client(_baseloop, muduo::net::InetAddress(sip, port), "MuduoClient"), _downlatch(1) // 初始化计数器为1
        {
        }

        virtual void Connect() override // 连接服务器
        {
            // 用bind解决多一个this指针问题
            // 设置回调函数
            _client.setConnectionCallback(std::bind(&MuduoClient::onConnection, this, std::placeholders::_1));
            _client.setMessageCallback(std::bind(&MuduoClient::onMessage, this,
                                                 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            // 连接服务器
            _client.connect(); // 非阻塞接口,connect进行完,链接不一定建立好了,要用countdownlatch限制
            _downlatch.wait(); // 进入阻塞状态
        }

        virtual void Shutdown() override // 关闭链接
        {
            _conn->Shutdown();
        }
        virtual bool Send(const BaseMessage::ptr &msg) override
        {
            if (Connected() == false)
            {
                ELOG("连接已断开,send失败");
                return false;
            }
            _conn->Send(msg);
            return true;
        }
        virtual BaseConnection::ptr Connection() override // 获取链接
        {
            return _conn;
        }
        virtual bool Connected() override // 链接是否正常}
        {
            return _conn->Connected();
        }

    private:
        void onConnection(const muduo::net::TcpConnectionPtr &conn) // 链接建立时/断开时的回调方法
        {
            if (conn->connected())
            {
                std::cout << "链接建立\n";
                _conn = MuduoConnectionFactory::create(conn, _protocol); //!!!给_conn赋值后再唤醒主线程(_downlatch.countDown())
                _downlatch.countDown();                                  // 计数-- ,为零时结束阻塞
            }
            else
            {
                std::cout << "链接断开\n";
                _conn.reset();
            }
        }

        // 收到消息的处理函数  ,先检查前缀是否正确 ,再检查
        void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buff, muduo::Timestamp)
        {
            // 想要接受到数据,首先要触发可读事件;想要触发可读事件,就要先开始事件循环监控
            auto base_buff = BufferFactory::create(buff);
            while (1)
            {
                if (_protocol->CanProcessed(base_buff) == false)
                {
                    // 数据不足
                    // 防备缓冲区中数据有很多很多,但是因为数据错误,导致数据又不足一条完整消息的情况 , 这种情况直接关闭连接
                    if (base_buff->ReadableBytes() > maxDataSize)
                    {
                        conn->shutdown();
                        ELOG("缓冲区中数据过大");
                        return;
                    }
                    break;
                }

                // 能够处理数据
                BaseMessage::ptr msg;
                bool ret = _protocol->OnMessage(base_buff, msg); // msg中只有body和id和Mtype
                if (ret == false)
                {
                    // 数据有问题
                    conn->shutdown();
                    ELOG("缓冲区中数据错误");
                    return;
                }
                // 客户端只有一个连接
                if (_cb_message)
                    _cb_message(_conn, msg);
            };
        }

    private:
        const size_t maxDataSize = (1 << 16); // 64字节
        BaseProtocol::ptr _protocol;
        muduo::net::EventLoopThread _loopthread; // 封装好的eventloopthread ,里面有loop和thread
        BaseConnection::ptr _conn;               // 用来发信息
        muduo::CountDownLatch _downlatch;
        muduo::net::EventLoop *_baseloop;
        muduo::net::TcpClient _client; // EventLoop* loop,  const InetAddress& serverAddr  ,  const string& nameArg
    };

    class ClientFactory
    {
    public:
        template <typename... Args>
        static BaseClient::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoClient>(std::forward<Args>(args)...);
        }
    };

}
